// Google test includes
#include <gtest/gtest.h>
#include <gmock/gmock.h>

// Tested file
#include "Parsers/PostfixParser.h"

// Mocks and fakes
#include "MockNode.h"
#include "MockNodeFactory.h"

using namespace ::testing;

TEST(PostfixParserTest, ParseAtom)
{
	StrictMock<TestNodeFactory> factory;

	TestNodeAtom child("A");
	EXPECT_CALL(factory, createNodeAtom("A")).WillOnce(Return(&child));
	EXPECT_EQ(&child, postfixParse<TestNode>("A", factory));
}

TEST(PostfixParserTest, ParseNot)
{
	StrictMock<TestNodeFactory> factory;

	// Prepare children
	TestNodeAtom child("A");
	EXPECT_CALL(factory, createNodeAtom("A")).WillOnce(Return(&child));

	// Check parsing NOT
	TestNodeNot nodeNot;
	EXPECT_CALL(factory, createNodeNot(&child)).WillOnce(Return(&nodeNot));
	EXPECT_EQ(&nodeNot, postfixParse<TestNode>("A~", factory));
}

TEST(PostfixParserTest, ParseAnd)
{
	StrictMock<TestNodeFactory> factory;

	// Prepare children
	TestNodeAtom child0("A");
	EXPECT_CALL(factory, createNodeAtom("A")).WillOnce(Return(&child0));
	TestNodeAtom child1("B");
	EXPECT_CALL(factory, createNodeAtom("B")).WillOnce(Return(&child1));

	// Check parsing AND
	TestNodeAnd nodeAnd;
	EXPECT_CALL(factory, createNodeAnd(&child0, &child1)).WillOnce(Return(&nodeAnd));
	EXPECT_EQ(&nodeAnd, postfixParse<TestNode>("AB&", factory));
}

TEST(PostfixParserTest, ParseOr)
{
	StrictMock<TestNodeFactory> factory;

	// Prepare children
	TestNodeAtom child0("A");
	EXPECT_CALL(factory, createNodeAtom("A")).WillOnce(Return(&child0));
	TestNodeAtom child1("B");
	EXPECT_CALL(factory, createNodeAtom("B")).WillOnce(Return(&child1));

	// Check parsing OR
	TestNodeOr nodeOr;
	EXPECT_CALL(factory, createNodeOr(&child0, &child1)).WillOnce(Return(&nodeOr));
	EXPECT_EQ(&nodeOr, postfixParse<TestNode>("AB|", factory));
}

TEST(PostfixParserTest, ParseComplex)
{
	StrictMock<TestNodeFactory> factory;

	// Prepare children
	TestNodeAtom child0("A");
	EXPECT_CALL(factory, createNodeAtom("A")).WillOnce(Return(&child0));
	TestNodeAtom child1("B");
	EXPECT_CALL(factory, createNodeAtom("B")).WillOnce(Return(&child1));
	TestNodeAtom child2("C");
	EXPECT_CALL(factory, createNodeAtom("C")).WillOnce(Return(&child2));
	TestNodeAtom child3("D");
	EXPECT_CALL(factory, createNodeAtom("D")).WillOnce(Return(&child3));

	// Prepare other nodes
	TestNodeOr nodeOr;
	EXPECT_CALL(factory, createNodeOr(&child0, &child1)).WillOnce(Return(&nodeOr));
	TestNodeAnd nodeAnd;
	EXPECT_CALL(factory, createNodeAnd(&child2, &child3)).WillOnce(Return(&nodeAnd));
	TestNodeNot nodeNot;
	EXPECT_CALL(factory, createNodeNot(&nodeAnd)).WillOnce(Return(&nodeNot));

	// Check parsing root AND
	TestNodeAnd root;
	EXPECT_CALL(factory, createNodeAnd(&nodeOr, &nodeNot)).WillOnce(Return(&root));
	EXPECT_EQ(&root, postfixParse<TestNode>("AB|CD&~&", factory));
}

TEST(PostfixParserTest, SyntaxError)
{
	StrictMock<TestNodeFactory> factory;

	EXPECT_THROW(postfixParse<TestNode>("0", factory), std::runtime_error);
}

TEST(PostfixParserTest, SuperfluousInputData)
{
	StrictMock<TestNodeFactory> factory;

	EXPECT_CALL(factory, createNodeAtom("A"));
	EXPECT_CALL(factory, createNodeAtom("B"));
	EXPECT_THROW(postfixParse<TestNode>("AB", factory), std::runtime_error);
}

